package cn.mehoyo.certificateGeneration.controller;

import cn.mehoyo.certificateGeneration.entity.CertificateResult;
import cn.mehoyo.certificateGeneration.entity.DnsChallengeInfo;
import cn.mehoyo.certificateGeneration.enums.ACME;
import cn.mehoyo.certificateGeneration.service.CertificateService;
import lombok.extern.slf4j.Slf4j;
import org.noear.snack.ONode;
import org.noear.snack.core.utils.StringUtil;
import org.noear.solon.annotation.*;
import org.noear.solon.core.handle.Context;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.X509Certificate;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 生成域名证书接口
 * @author llh
 */
@Slf4j
@Controller
@Mapping("/api/certificate")
public class CertificateController {

    private static final Map<String, Long> DOMAIN_LOCK = new ConcurrentHashMap<>();


    @Inject
    private CertificateService certificateService;

    @Inject("${solon.env}")
    private String env;

    /**
     * 第一步：获取 DNS 验证信息
     */
    @Post
    @Mapping("/dns-challenge")
    public Map<String, Object> getDnsChallenge(Map<String, String> request) {
        Map<String, Object> resp = new HashMap<>();
        String domain = request.get("domain");
        if (domain == null || domain.isEmpty()) {
            resp.put("success", false);
            resp.put("message", "域名不能为空");
            return resp;
        }

        if(DOMAIN_LOCK.putIfAbsent(domain, System.currentTimeMillis()) != null){
            resp.put("success", false);
            resp.put("message", "正在获取DNS验证信息或者生成证书，请稍后再试");
            return resp;
        }
        try {
            ACME acme = null;
            try{
                acme = ACME.valueOf(request.get("acme"));
            }catch (Exception e){
                acme = ACME.GOOGLE_TRUST;
            }


            DnsChallengeInfo challengeInfo = certificateService.getDnsChallengeInfo(domain,acme,request.get("eabKid"),request.get("eabHmacKey"));
            resp.put("success", true);
            resp.put("recordName", challengeInfo.getRecordName());
            resp.put("recordValue", challengeInfo.getRecordValue());
            resp.put("domain", challengeInfo.getDomain());
        } catch (Exception e) {
            resp.put("success", false);
            resp.put("message", "获取DNS验证信息失败: " + e.getMessage());
        }finally {
            DOMAIN_LOCK.remove(domain);
        }
        return resp;
    }

    /**
     * 第二步：根据 orderId 生成证书
     */
    @Post
    @Mapping("/generate")
    public Map<String, Object> generateCertificate(Map<String, String> request) {
        Map<String, Object> resp = new HashMap<>();
        String domain = request.get("domain");
        if (domain == null || domain.isEmpty()) {
            resp.put("success", false);
            resp.put("message", "domain不能为空");
            return resp;
        }
        if(DOMAIN_LOCK.putIfAbsent(domain, System.currentTimeMillis()) != null){
            resp.put("success", false);
            resp.put("message", "正在生成证书，请稍后再试");
            return resp;
        }
        try {
            CertificateResult result = certificateService.generateCertificate(domain);
            resp.put("success", true);
            resp.put("message", "证书生成成功");
            resp.put("downloadUrl", result.getDownLoadUrl());
        } catch (Exception e) {
            resp.put("success", false);
            resp.put("message", "证书生成失败: " + e.getMessage());
        }finally {
            DOMAIN_LOCK.remove(domain);
        }
        return resp;
    }

    @Get
    @Mapping("/download/{domain}")
    public File download(String domain, Context context){
        Path path = Paths.get("cert", domain + ".zip");
        File file = path.toFile();
        if(!file.exists()){
            context.status(404,"File Not Fond");
            return null;
        }else{
            return file;
        }
    }


    /**
     * 根据历史订单ID续签证书
     * 自动覆盖旧证书，或者根据业务修改生成新文件
     */
    @Post
    @Mapping("/renew")
    public Object renewCertificate(Map<String, String> request,Context ctx) {
        Map<String, Object> resp = new HashMap<>();
        try {
            String domain = request.get("domain");

            if ( domain == null || domain.isEmpty()) {
                resp.put("success", false);
                resp.put("message", "domain不能为空");
            }else{
                // 调用Service重新生成证书，覆盖旧证书
                certificateService.renewCertificate(domain);
                return download(domain,ctx);
            }

        } catch (Exception e) {
            log.error("证书续签失败:",e);
            resp.put("success", false);
            resp.put("message", "证书续签失败: " + e.getMessage());
        }
        return resp;
    }


    /**
     * 检测证书是否过期,如果过期重新生成证书
     * @param domain 域名
     * @param port ssl端口
     * @param expire 天数
     * @param ctx 上下文
     */
    @Get
    @Mapping("/checkExpirationTime")
    public void checkExpirationTime(String domain, Integer port, Integer expire,Context ctx){
       Map<String,Object> res = new HashMap<>();
        if (StringUtil.isEmpty(domain)){
            res.put("success", false);
            res.put("message", "域名不能为空");
        }
        else if (port == null){
            res.put("success", false);
            res.put("message", "端口不能为空");
        }
        else if (expire == null){
            res.put("success", false);
            res.put("message", "过期时间不能为空");
        }
        if(res.isEmpty()){
            SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            try (SSLSocket socket = (SSLSocket) factory.createSocket(domain, port)) {
                socket.startHandshake(); // 发起 TLS 握手
                X509Certificate[] certs = (X509Certificate[]) socket.getSession().getPeerCertificates();
                X509Certificate cert = certs[0];
                Instant notAfter = cert.getNotAfter().toInstant();
                ZonedDateTime time = notAfter.atZone(ZoneId.of("Asia/Shanghai"));
                LocalDate dateTime = time.toLocalDate().plus(-expire, ChronoUnit.DAYS);
                LocalDate now = LocalDate.now();
                if(dateTime.isAfter(now)){
                    res.put("success", true);
                    res.put("message", "证书未过期");
                }else{
                    renewCertificate(new HashMap<>(){{
                        put("domain",domain);
                    }},ctx);
                }
            }
            catch (Exception e){
                res.put("success", false);
                res.put("message", e.getMessage());
            }
        }
        if(!res.isEmpty()){
            ctx.contentType("application/json;charset=utf-8");
            ctx.outputAsJson(ONode.stringify(res));
            ctx.status(200);
        }

    }



}

